package com.telecom.restaurant.service;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.telecom.restaurant.constant.DefaultConstant;
import com.telecom.restaurant.constant.ManageThreadLocal;
import com.telecom.restaurant.dao.FoodDao;
import com.telecom.restaurant.dao.MenuDayDao;
import com.telecom.restaurant.dao.MenuDayToFoodDao;
import com.telecom.restaurant.domin.MealMenuResult;
import com.telecom.restaurant.domin.vo.DailyMenuVO;
import com.telecom.restaurant.domin.vo.MealMenuVO;
import com.telecom.restaurant.domin.vo.MenuFoodVO;
import com.telecom.restaurant.enums.FoodTypeEnum;
import com.telecom.restaurant.enums.MenuTypeEnum;
import com.telecom.restaurant.model.FoodPO;
import com.telecom.restaurant.model.MenuDayPO;
import com.telecom.restaurant.model.MenuDayToFoodPO;
import com.telecom.restaurant.model.MenuFoodPO;
import com.telecom.restaurant.model.param.MenuSearchParam;
import com.telecom.restaurant.util.DateUtil;
import com.telecom.restaurant.util.MoneyUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by qiqi on 2019/12/14.
 */
@Service
public class MenuService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private MenuDayDao menuDayDao;

    @Autowired
    private PictureService pictureService;

    @Autowired
    private FoodDao foodDao;

    @Autowired
    private FoodService foodService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private MenuDayToFoodDao menuDayToFoodPODao;

    @Autowired
    private ExcelService excelService;

    LoadingCache<String, DailyMenuVO> DAY_MENU_CACHE = CacheBuilder.newBuilder()
            .initialCapacity(10)
            .maximumSize(100)
            .expireAfterAccess(5, TimeUnit.DAYS)//设置时间对象没有被读/写访问则对象从内存中删除
            //CacheLoader类 实现自动加载
            .build(new CacheLoader<String, DailyMenuVO>() {
                @Override
                public DailyMenuVO load(String key) {
                    return getDailyMenuByStringKey(key);
                }
            });

    public MealMenuResult getMealMenuResult(MenuSearchParam param) {
        MealMenuResult result = new MealMenuResult();

        boolean lastAdd = false;

        // get menu day id
        MenuDayPO menuDayPO = menuDayDao.getOne(param);
        if (menuDayPO == null) {
            menuDayPO = new MenuDayPO();
            menuDayPO.setDate(param.getDate());
            menuDayPO.setRestaurantId(param.getRestaurantId());
            menuDayPO.setType(param.getType());
            menuDayDao.insert(menuDayPO);
            lastAdd = true;
        }

        this.setMenuDayToResult(menuDayPO, result);

        if (lastAdd == false) {
            // get food info
            int offset = 0;
            int limit = 50;
            // 循环从数据库查询
            while (true) {
                List<MenuFoodVO> foodPOList = foodDao.getFoodByMenuId(menuDayPO.getId(), offset, limit);
                if (foodPOList.isEmpty()) {
                    break;
                }
                for (MenuFoodVO food : foodPOList) {
                    int type = food.getType();
                    if (type == FoodTypeEnum.PACKAGE_DISH.getCode()) {
                        if (result.getPackageDish() == null) {
                            result.setPackageDish(new ArrayList<MenuFoodVO>());
                        }
                        result.getPackageDish().add(food);

                    } else if (type == FoodTypeEnum.PACKAGE_MAIN.getCode()) {
                        if (result.getPackageMain() == null) {
                            result.setPackageMain(new ArrayList<MenuFoodVO>());
                        }
                        result.getPackageMain().add(food);
                    } else if (type == FoodTypeEnum.OTHER_DISH.getCode()) {
                        if (result.getOtherDish() == null) {
                            result.setOtherDish(new ArrayList<MenuFoodVO>());
                        }
                        result.getOtherDish().add(food);
                    } else if (type == FoodTypeEnum.OTHER_MAIN.getCode()) {
                        if (result.getOtherMain() == null) {
                            result.setOtherMain(new ArrayList<MenuFoodVO>());

                        }
                        result.getOtherMain().add(food);
                    }
                }
                if (foodPOList.size() < limit) {
                    break;
                } else {
                    offset += limit;
                }
            }
        }

        return result;
    }

    public MenuDayPO getOrInsertMenuDay(MenuSearchParam param) {
        // get menu day id
        MenuDayPO menuDayPO = menuDayDao.getOne(param);
        if (menuDayPO == null) {
            menuDayPO = new MenuDayPO();
            menuDayPO.setDate(param.getDate());
            menuDayPO.setRestaurantId(param.getRestaurantId());
            menuDayPO.setType(param.getType());
            menuDayDao.insert(menuDayPO);
        }

        return menuDayPO;
    }


    private void setMenuDayToResult(MenuDayPO menuDayPO, MealMenuResult result) {
        if (result == null) {
            result = new MealMenuResult();
        }

        result.setType(menuDayPO.getType());
        result.setMenuId(menuDayPO.getId());
        result.setDate(DateUtil.convertDate2DateString(menuDayPO.getDate()));
    }

    /**
     * 查询某一天的菜单
     *
     * @param restaurantId
     * @param aimDayNo
     * @return
     */
    public DailyMenuVO getOneDayMenu(Long restaurantId, int aimDayNo) {
        DailyMenuVO result = null;

        int toDayDayNo = orderService.returnDayIfCanOrder();
        // 当前非下单时间
        Date date = getThisWorkDayByNo(aimDayNo);

        if (toDayDayNo < 0 || toDayDayNo != aimDayNo) {
            // read from cache
            String key = getStringKey(restaurantId, date);
            try {
                result = DAY_MENU_CACHE.get(key);
            } catch (Exception e) {
                logger.error("getOneDayMenu error. key:{}", key, e);

            }
        }
        if (result == null) {
            // 查数据库  今天的菜单需要更新库存
            result = getDailyMenu(date, restaurantId);
        }
        return result;
    }

    private String getStringKey(Long restaurantId, Date aimDate) {
        return restaurantId.toString() + "|" + DateUtil.convertDate2DateString(aimDate);
    }

    public void updateCache(Long menuId) {
        MenuDayPO menuDayPO = menuDayDao.selectByPrimaryKey(menuId);
        if (menuDayPO != null) {
            updateCache(menuDayPO.getRestaurantId(), menuDayPO.getDate());
        }
    }

    public void updateCache(Long restaurantId, Date aimDate) {
        String key = getStringKey(restaurantId, aimDate);
        DAY_MENU_CACHE.invalidate(key);
    }

//    public WeekMenuVO getWeekMenuFromCache(Long restaurantId) {
//        // 先查缓存
//        WeekMenuVO weekMenuVO = this.WEEK_MENU_CACHE.getIfPresent(restaurantId);
//        if (weekMenuVO == null) {
//            try {
//                weekMenuVO = WEEK_MENU_CACHE.get(restaurantId);
//            } catch (Exception e) {
//                logger.error("get error from loading cache, resId:{}", restaurantId, e);
//            }
//        } else {
//            // 如果可以下单，需要重新查询当天的库存
//            int dateNo = orderService.returnDayIfCanOrder();
//            if (dateNo >= 0) {
//                // 修改今天的库存
//                DailyMenuVO todayMenu = getDailyMenu(new Date(), restaurantId);
//                switch (dateNo) {
//                    case 0:
//                        weekMenuVO.setMonday(todayMenu);
//                        break;
//                    case 1:
//                        weekMenuVO.setTuesday(todayMenu);
//                        break;
//                    case 2:
//                        weekMenuVO.setWednesday(todayMenu);
//                        break;
//                    case 3:
//                        weekMenuVO.setThursday(todayMenu);
//                        break;
//                    case 4:
//                        weekMenuVO.setFriday(todayMenu);
//                        break;
//                }
//            }
//        }
//        if (weekMenuVO == null) {
//            weekMenuVO = constructWeekMenu(restaurantId);
//        }
//        return weekMenuVO;
//    }


//    private WeekMenuVO constructWeekMenu(Long restaurantId) {
//
//        WeekMenuVO weekMenuVO = new WeekMenuVO();
//        // 获取本周的周一周二周三周四周五
//        List<Date> workDayOfThisWeek = this.getThisWeekWorkDay();
//
//        weekMenuVO.setMonday(getDailyMenu(workDayOfThisWeek.get(0), restaurantId));
//        weekMenuVO.setTuesday(getDailyMenu(workDayOfThisWeek.get(1), restaurantId));
//        weekMenuVO.setWednesday(getDailyMenu(workDayOfThisWeek.get(2), restaurantId));
//        weekMenuVO.setThursday(getDailyMenu(workDayOfThisWeek.get(3), restaurantId));
//        weekMenuVO.setFriday(getDailyMenu(workDayOfThisWeek.get(4), restaurantId));
//
//        return weekMenuVO;
//    }

    /**
     * 处理string key  1|2019-12-12
     *
     * @param key
     * @return
     */
    private DailyMenuVO getDailyMenuByStringKey(String key) {
        String[] array = key.split("\\|");
        Long restaurantId = Long.valueOf(array[0]);
        Date date = DateUtil.convertString2Date(array[1]);

        return getDailyMenu(date, restaurantId);
    }

    /**
     * 查询某一天的菜单
     *
     * @return
     */
    private DailyMenuVO getDailyMenu(Date day, Long restaurantId) {
        DailyMenuVO dailyMenuVO = new DailyMenuVO();
        dailyMenuVO.setDate(DateUtil.convertDate2DateString(day));

        List<MenuFoodPO> menuFoodPOList = menuDayToFoodPODao.selectByMenuDate(day, restaurantId);
        if (CollectionUtils.isEmpty(menuFoodPOList)) {
            return dailyMenuVO;
        }
        // get Food
        Set<Long> foodIdList = new HashSet<>();
        for (MenuFoodPO menuFoodPO : menuFoodPOList) {
            foodIdList.add(menuFoodPO.getFoodId());
        }
        Map<Long, FoodPO> foodMap = foodService.getFoodByIds(foodIdList);
        for (MenuFoodPO menuFoodPO : menuFoodPOList) {
            MealMenuVO mealMenu = null;

            if (menuFoodPO.getType() == MenuTypeEnum.BREAKFAST.getCode()) {
                // 早餐
                if (dailyMenuVO.getBreakfast() == null) {
                    dailyMenuVO.setBreakfast(new MealMenuVO());
                }
                mealMenu = dailyMenuVO.getBreakfast();
            } else if (menuFoodPO.getType() == MenuTypeEnum.LUNCH.getCode()) {
                // 午餐
                if (dailyMenuVO.getLunch() == null) {
                    dailyMenuVO.setLunch(new MealMenuVO());
                }
                mealMenu = dailyMenuVO.getLunch();

            } else {
                // 晚餐
                if (dailyMenuVO.getDinner() == null) {
                    dailyMenuVO.setDinner(new MealMenuVO());
                }
                mealMenu = dailyMenuVO.getDinner();
            }
            // 添加到菜单里
            addToMealMenu(mealMenu, menuFoodPO, foodMap.get(menuFoodPO.getFoodId()));
        }

        // 固定主食
        addPackageMain(dailyMenuVO.getDinner());

        return dailyMenuVO;
    }

    private void addToMealMenu(MealMenuVO mealMenu, MenuFoodPO menuFoodPO, FoodPO food) {
        if (menuFoodPO == null || food == null) {
            return;
        }
        if (food.getType() == FoodTypeEnum.PACKAGE_DISH.getCode()) {
            if (mealMenu.getPackageDish() == null) {
                mealMenu.setPackageDish(new ArrayList<MenuFoodVO>());
            }
            mealMenu.getPackageDish().add(convertMenuFoodToVO(menuFoodPO, food));

        } else if (food.getType() == FoodTypeEnum.PACKAGE_MAIN.getCode()) {
            if (mealMenu.getPackageMain() == null) {
                mealMenu.setPackageMain(new ArrayList<MenuFoodVO>());
            }
            mealMenu.getPackageMain().add(convertMenuFoodToVO(menuFoodPO, food));
        } else if (food.getType() == FoodTypeEnum.OTHER_DISH.getCode()) {
            if (mealMenu.getOtherDish() == null) {
                mealMenu.setOtherDish(new ArrayList<MenuFoodVO>());
            }
            mealMenu.getOtherDish().add(convertMenuFoodToVO(menuFoodPO, food));

        } else {
            if (mealMenu.getOtherMain() == null) {
                mealMenu.setOtherMain(new ArrayList<MenuFoodVO>());
            }
            mealMenu.getOtherMain().add(convertMenuFoodToVO(menuFoodPO, food));
        }
    }

    private void addPackageMain(MealMenuVO mealMenu) {
        if (mealMenu == null) {
            return;
        }
        boolean addRice = true;
        boolean addMantou = true;
        if (CollectionUtils.isNotEmpty(mealMenu.getPackageMain())) {
            for (MenuFoodVO menuFoodVO : mealMenu.getPackageMain()) {
                if (DefaultConstant.PACKAGE_MAIN_MANTOU.getName().equals(menuFoodVO.getName())) {
                    addMantou = false;
                }
                if (DefaultConstant.PACKAGE_MAIN_RICE.getName().equals(menuFoodVO.getName())) {
                    addRice = false;
                }
            }
        }
        if (mealMenu.getPackageMain() == null) {
            mealMenu.setPackageMain(new ArrayList<MenuFoodVO>(2));
        }
        if (addRice) {
            mealMenu.getPackageMain().add(DefaultConstant.PACKAGE_MAIN_RICE);
        }
        if (addMantou) {
            mealMenu.getPackageMain().add(DefaultConstant.PACKAGE_MAIN_MANTOU);
        }
    }


    public List<Date> getThisWeekWorkDay() {

        List<Date> workDay = new ArrayList<>(5);

        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        // 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        // 获得当前日期是一个星期的第几天
        int day = cal.get(Calendar.DAY_OF_WEEK);
        // 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
        for (int i = 0; i < 5; i++) {
            workDay.add(cal.getTime());
            cal.add(Calendar.DATE, 1);
        }
        return workDay;
    }


    public Date getThisWorkDayByNo(int no) {

        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        // 判断要计算的日期是否是周日，如果是则减一天计算周六的，否则会出问题，计算到下一周去了
        int dayWeek = cal.get(Calendar.DAY_OF_WEEK);// 获得当前日期是一个星期的第几天
        if (1 == dayWeek) {
            cal.add(Calendar.DAY_OF_MONTH, -1);
        }
        // 设置一个星期的第一天，按中国的习惯一个星期的第一天是星期一
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        // 获得当前日期是一个星期的第几天
        int day = cal.get(Calendar.DAY_OF_WEEK);
        // 根据日历的规则，给当前日期减去星期几与一个星期第一天的差值
        cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - day);
        cal.add(Calendar.DATE, no);
        return cal.getTime();
    }

    private MenuFoodVO convertMenuFoodToVO(MenuFoodPO menuFoodPO, FoodPO food) {

        MenuFoodVO menuFoodVO = new MenuFoodVO();
        menuFoodVO.setId(menuFoodPO.getId());
        menuFoodVO.setRestaurantId(food.getRestaurantId());
        menuFoodVO.setName(food.getName());
        menuFoodVO.setPicture(pictureService.getPicturePath(food.getPicture()));
        menuFoodVO.setPrice(food.getPrice());
        menuFoodVO.setType(food.getType());
        menuFoodVO.setStock(menuFoodPO.getCount());

        return menuFoodVO;
    }

    public void importExcelMenu(File newFile) {

        Workbook book = excelService.readExcel(newFile.getAbsolutePath());

        List<String> dataList = new ArrayList<>();

        StringBuilder line = new StringBuilder();

        for (int sheetIndex = 0; sheetIndex < 3; sheetIndex++) {
            Sheet sheet = book.getSheetAt(sheetIndex);
            //获得当前sheet的开始行
            int firstRowNum = 1;
            int lastRowNum = sheet.getLastRowNum();
            for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) {
                line.delete(0, line.length());
                //获得当前行
                // 转换成：日期|菜单类型（早、中、晚）|菜品类型|名称 的
                Row row = sheet.getRow(rowNum);
                if (row == null) {
                    continue;
                }
                Cell firstCell = row.getCell(0);
                if (firstCell == null) {
                    break;
                }

                firstCell.setCellType(CellType.STRING);
                String date = firstCell.getStringCellValue().trim();
                if (StringUtils.isBlank(date)) {
                    break;
                }
                line.append(date).append("|"); // date
                line.append(sheetIndex + 1).append("|"); // 菜单类型

                String typeString = row.getCell(1).getStringCellValue().trim();
                int type = FoodTypeEnum.OTHER_DISH.getCode();
                if ("主食".equals(typeString) || "热菜".equals(typeString)) {
                    type = FoodTypeEnum.PACKAGE_DISH.getCode();
                }
                line.append(type).append("|");
                line.append(row.getCell(2).getStringCellValue().trim());
                if (type == FoodTypeEnum.OTHER_DISH.getCode() && sheetIndex == MenuTypeEnum.DINNER.getCode() - 1) {
                    Cell priceCell = row.getCell(3);
                    if (priceCell == null) {
                        continue;
                    }
                    priceCell.setCellType(CellType.STRING);
                    String stringPrice = priceCell.getStringCellValue().trim();

                    line.append("|").append(stringPrice);
                }
                String oneLine = line.toString();
                dataList.add(oneLine);

            }
        }
        this.dealExcelData(dataList);
        newFile.deleteOnExit();
    }

    private void dealExcelData(List<String> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }

        Long restaurantId = ManageThreadLocal.restaurantId.get();
        Map<String, Date> dateMap = new HashMap<>();
        Map<String, Long> menuDayIdMap = new HashMap<>();

        for (String line : dataList) {

            String[] dataArray = line.split("\\|");
            String dateNo = dataArray[0];
            Date menuDate = dateMap.get(dateNo);
            if (menuDate == null) {
                menuDate = this.getDateByDateNo(dateNo);
                dateMap.put(dateNo, menuDate);
            }

            String menuDayKey = line.substring(0, 7);
            Long menuDayId = menuDayIdMap.get(menuDayKey);
            if (menuDayId == null) {
                MenuDayPO menuDayPO = getOrInsertMenuDay(restaurantId, menuDate, Integer.valueOf(dataArray[1]));
                if (menuDayPO != null) {
                    menuDayId = menuDayPO.getId();
                    menuDayIdMap.put(menuDayKey, menuDayId);
                } else {
                    // 出错情况，直接略过
                    continue;
                }
            }

            Integer price = null;
            if (dataArray.length == 5) {
                price = MoneyUtil.convertStringYun2Fen(dataArray[4]);
            }

            FoodPO foodPO = foodService.getOrInsertFood(restaurantId,
                    dataArray[3], Integer.valueOf(dataArray[2]), price);


            MenuDayToFoodPO menuDayToFoodPO = new MenuDayToFoodPO();
            menuDayToFoodPO.setMenuDayId(menuDayId);
            menuDayToFoodPO.setFoodId(foodPO.getId());
            menuDayToFoodPO.setCount(100);

            menuDayToFoodPODao.insert(menuDayToFoodPO);
        }
    }

    private Date getDateByDateNo(String dateNo) {
        Calendar calendar = new GregorianCalendar(1900, 0, -1);
        calendar.add(Calendar.DAY_OF_YEAR, Integer.valueOf(dateNo));

        return calendar.getTime();
    }

    public MenuDayPO getOrInsertMenuDay(Long restaurantId, Date menuDate, Integer type) {

        MenuSearchParam param = new MenuSearchParam();
        param.setRestaurantId(restaurantId);
        param.setDate(menuDate);
        param.setType(type);
        MenuDayPO menuDayPO = this.getOrInsertMenuDay(param);
        return menuDayPO;
    }
}
